package services

import (
	"context"
	"errors"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	"gorm.io/gen"
	"gorm.io/gen/field"
	"gorm.io/gorm"
	"mall_frontend_api/biz/config"
	"mall_frontend_api/biz/dal"
	"mall_frontend_api/biz/handler/base"
	"mall_frontend_api/biz/model"
	"mall_frontend_api/biz/model/user_address"
	"mall_frontend_api/biz/pack"
	"mall_frontend_api/biz/util"
)

type UserAddressService struct {
	ctx    context.Context
	common *base.Response
	dao    *dal.InitDAO
}

func NewUserAddressService(ctx context.Context, common *base.Response) *UserAddressService {
	return &UserAddressService{ctx: ctx, common: common, dao: dal.NewInitDAO()}
}

func (c *UserAddressService) GetUserAddressList(req *user_address.GetUserAddressListRequest) ([]*user_address.UserAddress, int64, *user_address.UserAddress, error) {
	defaultInfo, err := c.dao.UserAddressDAO.GetByConditions(c.ctx, []gen.Condition{
		dal.UserAddress.UserID.Eq(c.common.GetUserInfo().ID),
		dal.UserAddress.Default.Eq(1),
	})
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		hlog.CtxErrorf(c.ctx, "GetUserDefaultAddressList error: %v", err)
		return nil, 0, nil, err
	}
	list, total, err := c.dao.UserAddressDAO.GetListWithTotal(c.ctx, &dal.UserAddressListParams{
		Condition: []gen.Condition{
			dal.UserAddress.UserID.Eq(c.common.GetUserInfo().ID),
		},
		Page: c.common.GetPage(),
		Size: c.common.GetSize(),
		Sort: []field.Expr{dal.UserAddress.CreatedAt.Desc()},
	})
	if err != nil {
		hlog.CtxErrorf(c.ctx, "GetUserAddressList error: %v", err)
		return nil, 0, nil, err
	}
	var regionsIDs []int64
	for _, m := range list {
		regionsIDs = append(regionsIDs, m.ReceiverRegionID, m.ReceiverCityID, m.ReceiverProvinceID)
	}
	if defaultInfo != nil {
		regionsIDs = append(regionsIDs, defaultInfo.ReceiverRegionID, defaultInfo.ReceiverCityID, defaultInfo.ReceiverProvinceID)
	}
	regionMap, err := c.dao.RegionDAO.MGet(c.ctx, util.Unique(regionsIDs))
	if err != nil {
		hlog.CtxErrorf(c.ctx, "MGetRegion error: %v", err)
		return nil, 0, nil, err
	}

	return pack.UserAddressList(list, regionMap, defaultInfo), total, pack.UserAddress(defaultInfo, regionMap), nil
}

func (c *UserAddressService) CreateUserAddress(req *user_address.CreateUserAddressRequest) error {
	if err := c.dao.Q.Transaction(func(tx *dal.Query) error {
		var isDefault int64 = 0
		if req.Default {
			condition := []gen.Condition{
				dal.UserAddress.UserID.Eq(c.common.GetUserInfo().ID),
				dal.UserAddress.BizID.Eq(c.common.GetBizID()),
				dal.UserAddress.Default.Eq(1),
			}
			if err := c.dao.UserAddressDAO.UpdateWithTx(c.ctx, tx, condition, []field.AssignExpr{dal.UserAddress.Default.Value(0)}); err != nil {
				hlog.CtxErrorf(c.ctx, "SetDefaultUserAddress cancel default error: %v", err)
			}
			isDefault = 1
		}
		data := &model.UserAddress{
			ReceiverName:       req.GetReceiverName(),
			ReceiverEmail:      req.GetReceiverEmail(),
			ReceiverProvinceID: req.GetReceiverProvinceID(),
			UserID:             c.common.GetUserInfo().ID,
			ReceiverAddress:    req.GetReceiverAddress(),
			ReceiverRegionID:   req.GetReceiverRegionID(),
			ReceiverCityID:     req.GetReceiverCityID(),
			ReceiverMobile:     req.GetReceiverMobile(),
			BizID:              config.Values.BizID,
			CreatedBy:          pack.GetUserName(c.common.GetUserInfo()),
			Default:            isDefault,
		}
		if _, err := c.dao.UserAddressDAO.CreateWithTx(c.ctx, tx, data); err != nil {
			hlog.CtxErrorf(c.ctx, "CreateUserAddress error: %v", err)
			return err
		}
		return nil
	}); err != nil {
		hlog.CtxErrorf(c.ctx, "CreateUserAddress error: %v", err)
		return err
	}

	return nil
}

func (c *UserAddressService) UpdateUserAddress(req *user_address.UpdateUserAddressRequest) error {
	if err := c.dao.Q.Transaction(func(tx *dal.Query) error {
		var isDefault int64 = 0
		if req.Default {
			condition := []gen.Condition{
				dal.UserAddress.UserID.Eq(c.common.GetUserInfo().ID),
				dal.UserAddress.BizID.Eq(c.common.GetBizID()),
				dal.UserAddress.Default.Eq(1),
			}
			if err := c.dao.UserAddressDAO.UpdateWithTx(c.ctx, tx, condition, []field.AssignExpr{dal.UserAddress.Default.Value(0)}); err != nil {
				hlog.CtxErrorf(c.ctx, "SetDefaultUserAddress cancel default error: %v", err)
			}
			isDefault = 1
		}
		if err := c.dao.UserAddressDAO.UpdateWithTx(c.ctx, tx,
			[]gen.Condition{dal.UserAddress.ID.Eq(req.ID)},
			[]field.AssignExpr{
				dal.UserAddress.ReceiverAddress.Value(req.GetReceiverAddress()),
				dal.UserAddress.ReceiverEmail.Value(req.GetReceiverEmail()),
				dal.UserAddress.ReceiverMobile.Value(req.GetReceiverMobile()),
				dal.UserAddress.ReceiverProvinceID.Value(req.GetReceiverProvinceID()),
				dal.UserAddress.ReceiverName.Value(req.GetReceiverName()),
				dal.UserAddress.ReceiverCityID.Value(req.GetReceiverCityID()),
				dal.UserAddress.ReceiverRegionID.Value(req.GetReceiverRegionID()),
				dal.UserAddress.UpdatedBy.Value(pack.GetUserName(c.common.GetUserInfo())),
				dal.UserAddress.Default.Value(isDefault),
			}); err != nil {
			hlog.CtxErrorf(c.ctx, "UserAddress error: %v", err)
			return err
		}

		return nil
	}); err != nil {
		hlog.CtxErrorf(c.ctx, "UpdateUserAddress Transaction error: %v", err)
		return err
	}

	return nil
}

func (c *UserAddressService) DeleteUserAddress(req *user_address.DeleteUserAddressRequest) error {
	if err := c.dao.UserAddressDAO.Delete(c.ctx, []gen.Condition{
		dal.UserAddress.ID.In(req.GetIDs()...),
	}); err != nil {
		hlog.CtxErrorf(c.ctx, "DeleteUserAddress error: %v", err)
		return err
	}
	return nil
}

func (c *UserAddressService) SetDefaultUserAddress(req *user_address.SetDefaultUserAddressRequest) error {
	if err := c.dao.Q.Transaction(func(tx *dal.Query) error {
		condition := []gen.Condition{
			dal.UserAddress.UserID.Eq(c.common.GetUserInfo().ID),
			dal.UserAddress.BizID.Eq(c.common.GetBizID()),
			dal.UserAddress.Default.Eq(1),
		}
		if err := c.dao.UserAddressDAO.UpdateWithTx(c.ctx, tx, condition, []field.AssignExpr{dal.UserAddress.Default.Value(0)}); err != nil {
			hlog.CtxErrorf(c.ctx, "SetDefaultUserAddress cancel default error: %v", err)
		}
		setCondition := []gen.Condition{
			dal.UserAddress.ID.Eq(req.GetID()),
		}
		if err := c.dao.UserAddressDAO.UpdateWithTx(c.ctx, tx, setCondition, []field.AssignExpr{dal.UserAddress.Default.Value(1)}); err != nil {
			hlog.CtxErrorf(c.ctx, "SetDefaultUserAddress set default error: %v", err)
			return err
		}

		return nil
	}); err != nil {
		hlog.CtxErrorf(c.ctx, "SetDefaultUserAddress Transaction error: %v", err)
		return err
	}

	return nil
}
